/*
 * Copyright © 2003-2018 Acronis International GmbH.
 */

#ifndef _PCS_ERROR_H_
#define _PCS_ERROR_H_ 1

#include "pcs_types.h"

typedef enum {
	PCS_ERR_OK		= 0,	/* No error */
	PCS_ERR_NOMEM		= 1,	/* Out of memory: malloc failure */
	PCS_ERR_PROTOCOL	= 2,	/* Fatal protocol error. Some condition, which should happen
					 * only if we have some bug in protocol implementation
					 */
	PCS_ERR_AUTH		= 3,	/* Authentication failure due to wrong credentials */
	PCS_ERR_NET		= 4,	/* Misc network error */
	PCS_ERR_NOSPACE		= 5,	/* ENOSPC/EDQUOT while local file io */
	PCS_ERR_IO		= 6,	/* Misc error while local file io */
	PCS_ERR_LOST_LOCK	= 7,	/* CN did not get response from MDS for lease update,
					 * it is generated by CN itself, sort of self-fencing
					 * Probably, useless and should be removed.
					 */

	PCS_ERR_NOT_FOUND	= 8,	/* Requested object not found */
	PCS_ERR_INTERRUPTED	= 9,	/* The operation was interrupted, should be retried */
	PCS_ERR_NET_ABORT	= 10,	/* Message dropped due to abort of network connection */
	PCS_ERR_CONNECT_TIMEOUT	= 11,	/* Failed connect() */
	PCS_ERR_AUTH_TIMEOUT	= 12,	/* Authentication failure due to timeout */
	PCS_ERR_RESPONSE_TIMEOUT= 13,	/* Peer did not respond or did not hold deadline */
	PCS_ERR_WRITE_TIMEOUT	= 14,	/* Socket write() failed, peer is stuck or network is broken */

	PCS_ERR_CANCEL_REQUEST	= 18,	/* Request was cancelled by user */
	PCS_ERR_CANCEL_IO	= 19,	/* IO request was cancelled */

	PCS_ERR_LEASE_REQUIRED	= 20,	/* Lease required */
	PCS_ERR_LEASE_EXPIRED	= 21,	/* Lease is expired */
	PCS_ERR_LEASE_CONFLICT	= 22,	/* Lease request conflicts with another lease */
	PCS_ERR_INV_PATH	= 23,	/* The path is invalid. Usually means an attempt to make a directory a subdirectory of itself. */
	PCS_ERR_NOT_DIR		= 24,	/* Attempt to read non-directory */
	PCS_ERR_IS_DIR		= 25,	/* Attempt to access directory (resize/io) */
	PCS_ERR_NON_EMPTY_DIR	= 26,	/* Attempt to rename/delete non empty directory */
	PCS_ERR_ZERO_CHUNK	= 27,	/* The requested chunk was not written yet and contains zero data */
	PCS_ERR_INVALID		= 29,   /* Object is invalid */
	PCS_ERR_INV_PARAMS	= 30,	/* Invalid parameters */
	PCS_ERR_NO_ID		= 31,	/* Request from the client without ID */
	PCS_ERR_INVALID_ID	= 32,	/* The client or server ID is invalid or banned */
	PCS_ERR_NORES		= 33,	/* Not enough resources (too many requests) */
	PCS_ERR_UNAVAIL		= 34,	/* Service unavailable */
	PCS_ERR_BAD_CLUSTER	= 35,	/* The cluster id specified by client is invalid */
	PCS_ERR_READONLY	= 36,	/* Invalid operation on read-only object */
	PCS_ERR_PERM		= 37,	/* Permission denied */
	PCS_ERR_UNSUPPORTED	= 38,	/* Operation is not supported */

	PCS_ERR_TEMP_UNAVAIL    = 40,	/* The resource is temporary unavailable */
	PCS_ERR_INTEGRITY	= 41,	/* Not enough alive replicas available */
	PCS_ERR_INTEGRITY_FAIL	= 42,	/* Fatal. Returned by MDS to client, when it is known that
					 * some unsynced data could be lost.
					 */

	PCS_ERR_NO_STORAGE	= 50,	/* The number of chunk servers in cluster is less than the required number of replicas */
	PCS_ERR_NOT_ALLOWED	= 51,	/* Operation is not allowed due to licensing limitations */
	PCS_ERR_IO_HOLE		= 52,	/* Read operation encountered a hole */
	PCS_ERR_CFG_VERSION	= 60,	/* Configuration version mismatch */
	PCS_ERR_CLNT_VERSION	= 61,	/* Client version is incompatible with sever version (outdated) */
	PCS_ERR_EXISTS		= 70,	/* Specified object already exists */
	PCS_ERR_EPOCH_MISMATCH	= 72,	/* Object epoch mismatch due to concurrent update */
	PCS_ERR_GR_EPOCH_INVALID= 73,	/* Object geo replication epoch invalid */
	PCS_ERR_NO_DIR		= 75,	/* Name directory does not exists */
	PCS_ERR_DIR_INST_VER	= 76,	/* Name instance version mismatch */
	PCS_ERR_INST_EXISTS	= 77,   /* The same instance of the object already exists */
	PCS_ERR_INST_OUTDATED	= 78,   /* The newer version of the object already exists */
	PCS_ERR_INST_MISMATCH	= 79,   /* The instance version of the object does not match the requested one */
	PCS_ERR_CONTEXT_LOST	= 80,	/* Operation context is lost on server restart */
	PCS_ERR_NS_LEASE_BUSY	= 81,	/* Lease wasn't acquired due to other active lease */
	PCS_ERR_NS_LEASE_INVALID= 82,	/* Active lease doesn't have reference with id provided in the request */
	PCS_ERR_NS_LOCK_EXPIRED = 83,	/* Lock at object's name NS has already expired */

	PCS_ERR_CSD_STALE_MAP	= 100,	/* Old map (or no map) at CS */
	PCS_ERR_CSD_RO_MAP	= 101,	/* Write request with read-only map */
	PCS_ERR_CSD_WR_IN_PROGR	= 102,	/* Read only map is rejected due to write requests being processed */
	PCS_ERR_CSD_REPLICATING	= 103,	/* Attempt to read from unfinished replica */
	PCS_ERR_CSD_STALLED_REPL= 104,	/* Relplication stalled */
	PCS_ERR_CANCEL_KEEPWAIT	= 105,	/* IO request was canceled and redirected to another CS */
	PCS_ERR_CSD_LACKING	= 110,	/* Not enough CS servers available */
	PCS_ERR_CSD_DROPPED	= 120,	/* The CS server was dropped by administrator */
	PCS_ERR_MDS_NOT_MASTER	= 200,	/* The target MDS is not current master */
	PCS_ERR_MDS_EXIST       = 201,  /* The MDS with such id already exist in cluster */
	PCS_ERR_MDS_RM_TOOMANY  = 202,  /* Removing this MDS will make the cluster unusable */

	PCS_ERR_MDS_KVSTORE_GEN_OUTDATED = 203, /* Requested generation is too old */

	PCS_ERR_LICENSE_LIMIT	= 300,  /* Operation can't be completed due to license limitations */
	PCS_ERR_NO_LICENSE	= 301,  /* No active license */

	PCS_ERR_SSL			  = 400, /* SSL protocol error */
	PCS_ERR_SSL_CERTIFICATE_REVOKED   = 401, /* Certificate revoked */
	PCS_ERR_SSL_CERTIFICATE_EXPIRED   = 402, /* Certificate expired */
	PCS_ERR_SSL_UNKNOWN_CA            = 403, /* Certificate issued by a CA the peer does not know and trust */
	PCS_ERR_PEER_CERTIFICATE_REJECTED = 404, /* The peer certificate has failed the verification */

	PCS_ERR_UNKNOWN		= 4095, /* Unknown error */
	PCS_ERR_MAX		= PCS_ERR_UNKNOWN
} pcs_err_t;

/* Get long description of the error */
PCS_API const char *pcs_strerror(pcs_err_t errnum);

/* Get short mnemonic */
PCS_API const char *pcs_errname(pcs_err_t errnum);

/* Render string describing errno (on Linux and Mac) or Windows system error code. Return 0 on success or positive error number */
PCS_API int pcs_sys_strerror_r(int err, char *buf, int buflen);
/* prints system error message to log */
PCS_API void pcs_log_syserror(int level, int err, const char *msg, ...);

/* ----------------------------------------------------------------------------------- */

/* Error code handling. "value" is one of error codes defined below,
 * all the components share one error namespace. System errnos are not used,
 * each subsystem, using syscalls, must recode errnos to one of PCS error codes.
 * "remote" means that "offender" is valid. "offender" is node id, where this error
 * was generated.
 *
 * XXX TODO there is one important case. Now "offender" is set when we have connection
 * to peer and that peer returned an error via RPC. This is wrong (in many situation):
 * we should return remote error, when communication with node fails due to
 * failure of network between us and peer, or the peer itself. This is important.
 * But tricky, we should remember that not all the communication channels should generate
 * remote error. F.e. failure of communication with MDS is local error for CS communicating
 * with MDS and remote error with that CS as offender for another nodes. It is easy to mess
 * up and I did mess it up form the very beginning. :-)
 */

struct _pcs_error_t
{
	unsigned int	value : 31, remote: 1;

	PCS_NODE_ID_T		offender;
};
typedef struct _pcs_error_t pcs_error_t;

static __inline void pcs_clear_error(pcs_error_t * err)
{
	err->value = 0;
}

static __inline int pcs_if_error(pcs_error_t const* err)
{
	return err->value != 0;
}

static __inline void pcs_copy_error(pcs_error_t * dst, pcs_error_t const* src)
{
	dst->value = src->value;
	dst->remote = src->remote;
	if (dst->remote)
		dst->offender = src->offender;
}

static __inline void pcs_copy_error_cond(pcs_error_t * dst, pcs_error_t const* src)
{
	if (src->value && !dst->value)
		pcs_copy_error(dst, src);
}

static __inline void pcs_set_local_error(pcs_error_t * status, int err)
{
	status->value = err;
	status->remote = 0;
}

int pcs_error_to_errno(pcs_error_t *);

static __inline void* pcs_err_ptr(int err)
{
	return (void*)(~(ULONG_PTR)err);
}

static __inline int pcs_ptr_err(void* ptr)
{
	return (int)(~(ULONG_PTR)ptr);
}

static __inline int pcs_is_ptr_err(void* ptr)
{
	return 0 < ~(ULONG_PTR)ptr && ~(ULONG_PTR)ptr <= PCS_ERR_MAX;
}

static __inline int pcs_is_ptr_err_or_null(void* ptr)
{
	return !ptr || pcs_is_ptr_err(ptr);
}

/* Convert errno on Linux/Mac or Windows error code to pcs_err_t */
PCS_API pcs_err_t pcs_errno_to_err(int err);

#endif /* _PCS_ERROR_H_ */
